BOJ_1943_동전 분배
2초니까 DP(Dynamic Programming) 아니겠지? 하고 DFS로 풀었다가 시간 초과 뚜드려 맞고 DP로 푼 문제
- DP[0] = true
- 동전의 가치와 개수로 만들 수 있는 금액을 true로 줘서 초기 세팅한다
- 반복문을 돌면서 true인 곳에(해당 금액을 만들 수 있다는 뜻) 현재 동전(바깥 반복문) * 현재 개수(안쪽 반복문)를 더한 금액을 true로 바꾼다
- 정확히 절반을 만들었다면 다른 절반도 만들 수 있다는 뜻이 되기 때문에 총액 / 2 를 만들었는지 확인하면 된다
디테일
이전 결과가 다음 결과에 영향을 미치기 때문에 탑다운 방식으로 풀어야 한다. 즉, 큰 금액부터 작은 금액으로 탐색해야 한다
반대로 하게 되면 예를 들어 1원이 1개 있을 때 dp[1원] = true로 주고 2를 만들러 가면 dp[1+1원] = true, dp[2 + 1원] = true ... 이렇게 모든 금액을 1원 1개로 만들 수 있게 되는 기적이 일어난다
금액 탐색 범위는 총액 / 2 - value 이하 0 초과로 했다. 0은 이미 초기 세팅 때 작업해주었고, 총액 / 2 - value 보다 크다면 value 가치의 동전을 1개도 더 더할 수 없기 때문이다
총액이 2로 떨어지지 않으면 둘로 나눌 수 없기 때문에 dp 탐색할 필요 없다
마찬가지로 총액 / 2 가 이미 true라면 dp 탐색하러 갈 필요가 없다
삽질
dp 배열을 50001로 주고 예외처리 안 해서 아웃오브바운드가 떴다 ㅎㅎ 초기 세팅할 때 동전의 배수로 50000을 넘어서까지 만들어질 수도 있기 때문에 50000 넘어가면 마킹 안 하게 했다
package BJO;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
public class BJO_1943_동전분배 {
static class Coin {
int value;
int count;
Coin(int value, int count) {
this.value = value;
this.count = count;
}
}
static int answer = 0;
static int money = 0;
static Coin[] coins;
static boolean[] dp;
/*
dp 배낭 문제
*/
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer st;
StringBuilder sb = new StringBuilder();
for (int t = 0; t < 3; t++) {
int N = Integer.parseInt(br.readLine());
answer = 0;
money = 0;
coins = new Coin[N];
dp = new boolean[50001]; // 최대 금액이 10만이다 절반만 보면 된다
dp[0] = true;
for (int i = 0; i < N; i++) {
st = new StringTokenizer(br.readLine());
int value = Integer.parseInt(st.nextToken());
int count = Integer.parseInt(st.nextToken());
// 총합을 구한다
money += value * count;
Coin coin = new Coin(value, count);
coins[i] = coin;
// 동전 가치의 개수 배수를 다 true로 준다
for (int j = 1; j <= count; j++) {
if (value * j > 50000) {
break;
}
dp[value * j] = true;
}
}
if (money % 2 == 0) { // 최종 금액이 2의 배수가 아니면 반으로 나눌 수 없음
if (dp[money / 2]) {
answer = 1;
} else {
dp(N, money);
}
}
sb.append(answer).append('\n');
}
System.out.println(sb);
}
static void dp(int n, int money) {
for (int i = 0; i < n; i++) {
int value = coins[i].value;
int count = coins[i].count;
for (int j = money / 2 - value; j > 0; j--) {
if (dp[j]) {
for (int k = 1; k <= count && j + value * k <= money / 2; k++) {
dp[j + value * k] = true;
if (j + value * k == money / 2) {
answer = 1;
}
}
}
}
}
}
}